package com.sinosoft.builder.mojo;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;

import com.sinosoft.builder.config.FileHelper;
import com.sinosoft.builder.generator.AssemblyException;

/**
 * 平台系统打包插件程序
 * 
 * @author 潘巍（Peter Pan）
 * @goal assembly
 * @requiresDependencyResolution compile
 * @phase generate-resources
 * @since 2010-8-16 上午09:22:04
 */
public class AssemblyMojo extends AbstractCDKMojo {
	/**
	 * Maven项目构建器
	 * 
	 * @component
	 */
	private MavenProjectBuilder mavenProjectBuilder;

	/**
	 * 用于在远程资源库中查找Artifacts
	 * 
	 * @component
	 */
	private ArtifactFactory factory;

	/**
	 * @component
	 */
	private ArtifactResolver resolver;

	/**
	 * 用于实现Archiver/UnArchiver
	 * 
	 * @component
	 */
	private ArchiverManager archiverManager;

	/**
	 * 本地资源库
	 * 
	 * @parameter expression="${localRepository}"
	 */
	private ArtifactRepository localRepository;

	/**
	 * List of Remote Repositories used by the resolver
	 * 
	 * @parameter expression="${project.remoteArtifactRepositories}"
	 */
	@SuppressWarnings("unchecked")
	protected List remoteRepositories;

	/**
	 * 关联的服务描述集合<br>
	 * 描述采用groupid:artifactid方式表示
	 * 
	 * @parameter
	 */
	private String attachedServices;

	/**
	 * Ear的lib目录
	 * 
	 * @parameter default-value=
	 *            "${project.build.directory}/ear-${project.version}/lib"
	 */
	private File earLibDirectory;

	/**
	 * Ear的目录
	 * 
	 * @parameter default-value=
	 *            "${project.build.directory}/ear-${project.version}"
	 */
	private File earDirectory;

	/**
	 * 用于存放扫描模块解压的源码目录
	 * 
	 * @parameter expression="${project.build.directory}/modules-src"
	 */
	private File modulesSrc;

	/**
	 * 用于存放扫描模块解压的jar目录
	 * 
	 * @parameter expression="${project.build.directory}/modules-jar"
	 */
	private File modulesJar;

	/**
	 * 用于编译类目录
	 * 
	 * @parameter expression="${project.build.directory}/pom.xml"
	 */
	private File generatedPom;

	/**
	 * 工程在第一个compile生命周期中被执行。
	 * 
	 * @parameter expression="${executedProject}"
	 * @readonly
	 */
	protected MavenProject executedProject;

	/**
	 * @parameter
	 */
	private boolean includeProjectDependencies = false;

	/**
	 * @parameter
	 */
	private String excludeProjectDependencies;

	/**
	 * 服务关联的Artifact
	 */
	private List<Artifact> serviceArtifacts = new ArrayList<Artifact>();

	/**
	 * 项目依赖的第三方包
	 */
	Map<String, Dependency> modulesDependencies = new HashMap<String, Dependency>();

	private FileHelper fileHelper = new FileHelper();

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		getLog().debug("调用AssemblyMojo组件");

		try {
			// 初始化服务所在的根Artifact
			createServiceArtifact();

			// 查询子模块，并解压
			// models是探测过的model的集合
			List<Model> models = new ArrayList<Model>();
			for (Artifact a : serviceArtifacts) {
				// 通过给定配置，构建要扫描的MaveProject对象
				MavenProject mp = mavenProjectBuilder.buildFromRepository(a,
						remoteRepositories, localRepository);
				models.addAll(extractModules(mp));
			}

			// 添加models到ear/lib下面
			addModelsToLib(models);

			// 移除不需要的依赖，不需要的依赖包括两部分（models和已设置的依赖）
			removeModelFromDependency(models);

			// 添加依赖到ear/lib下面
			addDependencyToLib();

			// 复制解压的资源复制到ear/war/META-INF/model下面
			copyResourceFileFromJars();

			// 设置已创建的项目
			setupGeneratedProject();

		} catch (AssemblyException ex) {
			throw new MojoFailureException(ex.getMessage(), ex);
		} catch (ProjectBuildingException ex) {
			throw new MojoExecutionException(ex.getMessage(), ex);
		}

	}

	/**
	 * 从解压的jar中，复制指定目录的文件到ear的指定位置（earDirectory）
	 */
	private void copyResourceFileFromJars() {
		// 遍历解压扫描过的jar目录
		if (modulesJar.exists() && modulesJar.isDirectory()) {
			File[] jarsDir = modulesJar.listFiles();
			File destMetadata = new File(earDirectory,"META-INF/metadata");
			if (jarsDir != null) {
				for (File jar : jarsDir) {
					if (jar.isDirectory()) {
						File md = new File(jar, "META-INF/metadata");
						if (md.exists() && md.isDirectory()) {
							fileHelper.copyResources(md, destMetadata,
									getLog());
						}
					}
				}
			}
		}
	}

	/**
	 * 复制扫描的模块的jar到ear/lib下面，排除设置不要的模块
	 * 
	 * @param models
	 * @throws MojoExecutionException
	 */
	private void addModelsToLib(List<Model> models)
			throws MojoExecutionException {
		Map<String, String> unwanted = new HashMap<String, String>();
		if (excludeProjectDependencies != null
				&& !"".equals(excludeProjectDependencies)) {
			String[] excludes = excludeProjectDependencies.trim().replaceAll(
					"\n\r", "").replaceAll("\n", "").split(",");
			for (String ex : excludes) {
				ex = ex.trim();
				if (ex != null && !"".equals(ex)) {
					getLog().info("从依赖中移除服务子模块" + ex);
					unwanted.put(ex, ex);
				}
			}
		}

		for (Model model : models) {
			if (!unwanted.containsKey(model.getGroupId() + ":"
					+ model.getArtifactId())) {
				// 复制服务的jar到modulesDirectory下面
				Artifact artifact = factory.createBuildArtifact(model
						.getGroupId(), model.getArtifactId(), model
						.getVersion(), model.getPackaging());
				try {
					resolver.resolve(artifact, remoteRepositories,
							localRepository);
					// 复制jar文件到ejb/lib下面
					fileHelper.copyResources(artifact.getFile(),
							earLibDirectory, getLog());
				} catch (ArtifactResolutionException e) {
					throw new MojoExecutionException("解析Artifact："
							+ artifact.toString() + "出现错误", e);
				} catch (ArtifactNotFoundException e) {
					throw new MojoExecutionException("无法找到Artifact："
							+ artifact.toString(), e);
				}
			}
		}
	}

	/**
	 * 复制依赖的jar到ear/lib下面
	 * 
	 * @throws MojoExecutionException
	 */
	private void addDependencyToLib() throws MojoExecutionException {
		addDependencyToLib(modulesDependencies, earLibDirectory);
	}

	private void addDependencyToLib(Map<String, Dependency> dep, File dir)
			throws MojoExecutionException {
		Artifact artifact = null;
		Dependency dependency;
		try {
			for (Iterator<Dependency> iterator = dep.values().iterator(); iterator
					.hasNext();) {
				dependency = iterator.next();
				artifact = factory.createBuildArtifact(dependency.getGroupId(),
						dependency.getArtifactId(), dependency.getVersion(),
						dependency.getType());

				resolver.resolve(artifact, remoteRepositories, localRepository);
				// 复制jar文件
				fileHelper.copyResources(artifact.getFile(), dir, getLog());
			}
		} catch (ArtifactResolutionException e) {
			throw new MojoExecutionException("解析Artifact："
					+ artifact.toString() + "出现错误", e);
		} catch (ArtifactNotFoundException e) {
			throw new MojoExecutionException("无法找到Artifact："
					+ artifact.toString(), e);
		}
	}

	/**
	 * 设置已创建的项目，设置依赖
	 * 
	 * @throws MojoExecutionException
	 */
	@SuppressWarnings("unchecked")
	private void setupGeneratedProject() throws MojoExecutionException {

		Map localProjectDependencies = new HashMap();

		Model generatedProject;
		try {
			MavenXpp3Reader reader = new MavenXpp3Reader();
			generatedProject = reader.read(new FileReader(project.getFile()));
		} catch (Exception e1) {
			throw new MojoExecutionException("无法读取本地POM", e1);
		}

		if (includeProjectDependencies) {
			Set activeProfilesIds = new HashSet();
			List activeProfiles = project.getActiveProfiles();
			if (activeProfiles != null) {
				for (Iterator activeProfilesItr = activeProfiles.iterator(); activeProfilesItr
						.hasNext();) {
					Profile activeProfile = (Profile) activeProfilesItr.next();
					activeProfilesIds.add(activeProfile.getId());
				}
			}

			List dependencies = generatedProject.getDependencies();
			if (dependencies != null) {
				for (Iterator itr = dependencies.iterator(); itr.hasNext();) {
					Dependency dependency = (Dependency) itr.next();
					localProjectDependencies.put(dependency.getManagementKey(),
							dependency);
				}
			}

			List profiles = generatedProject.getProfiles();
			if (profiles != null) {
				for (Iterator profilesItr = profiles.iterator(); profilesItr
						.hasNext();) {
					Profile profile = (Profile) profilesItr.next();
					if (activeProfilesIds.contains(profile.getId())) {
						List profileDependencies = profile.getDependencies();
						if (profileDependencies != null) {
							for (Iterator itr = profileDependencies.iterator(); itr
									.hasNext();) {
								Dependency dependency = (Dependency) itr.next();
								localProjectDependencies.put(dependency
										.getManagementKey(), dependency);
							}
						}
					}
				}
			}
		}

		generatedProject.getDependencies().addAll(modulesDependencies.values());
		writePom(generatedProject);

		localProjectDependencies.putAll(modulesDependencies);
		project.setDependencies(new ArrayList<Dependency>(
				localProjectDependencies.values()));
	}

	private void writePom(Model pom) throws MojoExecutionException {
		MavenXpp3Writer pomWriter = new MavenXpp3Writer();
		try {
			FileWriter out = new FileWriter(generatedPom);
			pomWriter.write(out, pom);
		} catch (IOException e) {
			throw new MojoExecutionException("写入创建的POM出现错误", e);
		}

	}

	/**
	 * 通过service的组合MavenProject获得子模块，同时获得子模块的依赖
	 * 
	 * @param mp
	 * @return
	 * @throws MojoExecutionException
	 */
	private List<Model> extractModules(MavenProject mp)
			throws MojoExecutionException {

		// 返回给执行程序的已探测的Model集合
		List<Model> models = new ArrayList<Model>();

		extractModel(mp.getModel(), models);

		return models;
	}

	/**
	 * 解析MavenProject对象，即pom对象
	 * 
	 * @param mp
	 * @param models
	 * @throws MojoExecutionException
	 */
	@SuppressWarnings("unchecked")
	private void extractModel(MavenProject mp, List<Model> models)
			throws MojoExecutionException {
		List<String> modules = mp.getModules();
		for (Iterator<String> iter = modules.iterator(); iter.hasNext();) {
			String moduleName = iter.next();
			getLog().info("服务集合项目包含模块：" + moduleName);
			Model model;

			// 为子模块，创建一个Artifact
			Artifact artifactBuilder;
			try {
				artifactBuilder = factory
						.createProjectArtifact(mp.getGroupId() + "."
								+ mp.getArtifactId(), moduleName, mp
								.getVersion());
				model = mavenProjectBuilder.buildFromRepository(
						artifactBuilder, remoteRepositories, localRepository)
						.getModel();
			} catch (Exception e) {
				throw new MojoExecutionException("无法读取模块的POM文件", e);
			}

			// 如果其类型是jar、ejb或者war时可以调用
			extractModel(model, models);

		}
	}

	/**
	 * 解析Model，将其依赖的model放入集合中
	 * 
	 * @param model
	 *            观察的model
	 * @param models
	 *            依赖的model集合
	 * @throws MojoExecutionException
	 */
	private void extractModel(Model model, List<Model> models)
			throws MojoExecutionException {
		// 1.如果是jar，表示其为一个服务组件，将其解压到组件的目录中，target/modules/下面；
		if ("jar".equalsIgnoreCase(model.getPackaging())
				|| "ejb".equalsIgnoreCase(model.getPackaging())) {
			getLog().debug("服务子模块项目" + model.getName() + "被执行");

			// 添加依赖，除test生命周期的依赖
			addDependenciesFromModel(model);

			// 添加模块
			models.add(model);

			// 复制服务的jar到modulesDirectory下面
			Artifact artifact = factory.createBuildArtifact(model.getGroupId(),
					model.getArtifactId(), model.getVersion(), model
							.getPackaging());
			try {
				// 获得jar
				resolver.resolve(artifact, remoteRepositories, localRepository);
				// 解压jar文件
				unpack(artifact.getFile(), new File(modulesJar, artifact
						.getFile().getName()));
			} catch (ArtifactResolutionException e) {
				throw new MojoExecutionException("解析Artifact："
						+ artifact.toString() + "出现错误", e);
			} catch (ArtifactNotFoundException e) {
				throw new MojoExecutionException("无法找到Artifact："
						+ artifact.toString(), e);
			}

			// 解压源码文件到target/modules-src
			try {
				// 获得源码的jar
				artifact = factory.createArtifactWithClassifier(model
						.getGroupId(), model.getArtifactId(), model
						.getVersion(), "jar", "sources");
				resolver.resolve(artifact, remoteRepositories, localRepository);
				// 解压源码文件
				unpack(artifact.getFile(), modulesSrc);
			} catch (ArtifactResolutionException e) {
				getLog()
						.warn("警告：解析源码的Artifact" + artifact.toString() + "出现错误");
			} catch (ArtifactNotFoundException e) {
				getLog().warn("警告：无法找到Artifact" + artifact.toString());
			}
		}

		// 2.如果是pom，表示其为一个Maven的POM，循环执行；
		else if ("pom".equals(model.getPackaging())) {
			Artifact a = factory
					.createBuildArtifact(model.getGroupId(), model
							.getArtifactId(), project.getArtifact()
							.getVersion(), "pom");
			try {
				MavenProject mp = mavenProjectBuilder.buildFromRepository(a,
						remoteRepositories, localRepository);

				extractModel(mp, models);

			} catch (ProjectBuildingException e) {
				throw new MojoExecutionException("解析Artifact：" + a.toString()
						+ "出现错误", e);
			}
		}
	}

	/**
	 * 添加Model中的依赖
	 * 
	 * @param model
	 * @param type
	 */
	@SuppressWarnings("unchecked")
	private void addDependenciesFromModel(Model model) {
		// 遍历目标的所有依赖
		for (Iterator<Dependency> iterator = model.getDependencies().iterator(); iterator
				.hasNext();) {
			Dependency dependency = iterator.next();
			getLog().debug(
					dependency.getClass().getName() + " : " + dependency
							+ ", 主键 : " + dependency.getManagementKey());
			// 同时为打包项目提供子模块的依赖，排除test依赖
			if (!"test".equals(dependency.getScope())
					&& !"provided".equals(dependency.getScope())) {
				if ("jar".equals(dependency.getType())) {
					modulesDependencies.put(dependency.getGroupId() + ":"
							+ dependency.getArtifactId(), dependency);
				}
			}
		}
	}

	/**
	 * 解压文件
	 * 
	 * @param file
	 * @param location
	 * @throws MojoExecutionException
	 */
	private void unpack(File file, File location) throws MojoExecutionException {
		getLog().debug(
				"解压文件" + file.getAbsolutePath() + "到"
						+ location.getAbsolutePath());
		try {
			location.mkdirs();
			UnArchiver unArchiver;
			unArchiver = archiverManager.getUnArchiver(file);
			unArchiver.setSourceFile(file);
			unArchiver.setDestDirectory(location);
			unArchiver.setOverwrite(true);
			unArchiver.extract();
		} catch (Exception e) {
			throw new MojoExecutionException("解压文件过程中出现错误：" + file + "到"
					+ location + "\r\n" + e.toString(), e);
		}
	}

	/**
	 * 从依赖集合中移除正在处理的子服务模块
	 * 
	 * @param models
	 */
	private void removeModelFromDependency(List<Model> models) {
		Set<String> unwanted = new HashSet<String>();
		// 排除已探测的依赖
		for (Iterator<Model> iter = models.iterator(); iter.hasNext();) {
			Model model = (Model) iter.next();
			getLog().info(
					"从依赖中移除服务子模块" + model.getGroupId() + ":"
							+ model.getArtifactId());
			unwanted.add(model.getGroupId() + ":" + model.getArtifactId());
		}

		// 移除默认的依赖
		if (excludeProjectDependencies != null
				&& !"".equals(excludeProjectDependencies)) {
			String[] excludes = excludeProjectDependencies.trim().replaceAll(
					"\n\r", "").replaceAll("\n", "").split(",");
			for (String ex : excludes) {
				ex = ex.trim();
				if (ex != null && !"".equals(ex)) {
					getLog().info("从依赖中移除服务子模块" + ex);
					unwanted.add(ex);
				}
			}
		}

		modulesDependencies.keySet().removeAll(unwanted);

	}

	/**
	 * 创建服务的根Artifact<br>
	 * 通过读取attachedServices配置，<br>
	 * 格式为groupId1:artifactId1,groupId2:artifactId2,...<br>
	 * 初始化serviceArtifacts对象
	 * 
	 * @throws AssemblyException
	 */
	private void createServiceArtifact() throws AssemblyException {
		if (attachedServices != null && !"".equals(attachedServices)) {
			String[] attachedServicesL = attachedServices.split(",");
			for (String sa : attachedServicesL) {
				String[] saL = sa.trim().split(":");
				if (saL.length != 2) {
					throw new AssemblyException("关联服务配置出现错误：" + sa);
				}
				Artifact a = factory.createBuildArtifact(saL[0], saL[1],
						project.getArtifact().getVersion(), "pom");
				serviceArtifacts.add(a);
			}
		}

	}

}
